package com.hoas.distributedlock.strategy.redis;

import com.hoas.distributedlock.core.DisributedLock;
import com.hoas.distributedlock.core.ILockName;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis方式实现的分布式锁
 * @Author 流云细水
 * @Mail zhangqingmu@yeah.net
 */
public class RedisLock implements DisributedLock {

    private Set<String> locks = new HashSet<String>();

    private JedisPool jedisPool;

    private String prefix = "disributedlock";

    private long expireSecond = 300L;

    public RedisLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public boolean lock(ILockName lockName, int times) {
        times = times < 1 ? 1: times;
        while (times > 0) {
            if (lock(lockName)){
                return true;
            }
            try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        return false;
    }

    public boolean lock(ILockName lockName) {


        String key = lockName(lockName);
        // 控制重入
        if (locks.contains( key )) {
            return true;
        }

        if(redis_setnx(key, setData(),expireSecond)) {

            locks.add( key);
            return true;

        }

        return false;
    }

    public void release(ILockName lockName) {

        redis_del(lockName(lockName));
        locks.remove( lockName.value());

    }

    public void release() {

        String[] keys = new String[locks.size()];
        locks.toArray(keys);
        redis_del(keys);
        locks.clear();

    }

    protected boolean redis_setnx(String key, String value, long exptime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String result = jedis.set(key, value, "NX", "EX", exptime);;
            if ((result != null) && ("OK".equals(result) || "+OK".equals(result))){
                //请求仅一次有效（防止短时间内的重放攻击）
                return true;
            }
            return false;
        } finally {
            jedis.close();
        }
    }

    protected void redis_del(String...keys) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            jedis.del( keys );

        } finally {
            jedis.close();
        }
    }

    protected String setData() {
        return "" + System.currentTimeMillis();
    }

    protected String lockName(ILockName lockName) {
        return prefix + ":" + lockName.value();
    }

    public void setExpireSecond(long expireSecond) {
        this.expireSecond = expireSecond;
    }

    public long getExpireSecond() {
        return expireSecond;
    }

    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

}
